# 1.局部变量与赋值
greeting = 'hello'

# '' 与"" 的区别："" 可以作为模板字面量使用
puts greeting

# 2.并行赋值
width, height, depth = [1000, 2250, 250]
puts height # 2250

# 3.字符串插值
str = "hello #{'dlrow'.reverse}"
puts str    # hello world


# 如果#{} 内收到的不是一个字符串类型的对象，那么这个对象会自动收到一个to_s 
# 消息以返回能顶替其位置的字符串，可以由此借机控制被替换对象的展示方式
o = Object.new
def o.to_s
    'a new object'
end

str1 = "here is #{o}"
puts str1
# here is a new object


#  4. 检查对象
# 在irb 模式下：
obj = Object.new 
def obj.inspect
    '[my object]'
end

obj # [my object]

# 5. 打印字符串
x = 128
while x < 1000
    puts "x is #{x}"
    x *= 2
end

# 5.可变参数方法
# 使用* 运算符，支持数目可变的参数
def join_with_commas(*words)
    words.join(',')
end

puts join_with_commas("one", "two", "three")
# one,two,three

# 一个方法只能使用一个可变参数，而常规参数可以放在可变参数的前后皆可
def join_with_commas1(before, *words, after)
    before + words.join(',') + after 
end

puts join_with_commas1('Testing:', 'one', 'two', 'three', '.')
# Testing:one,two,three.

# 发送消息的时候，* 运算符还可以吧每个数组元素当做单个参数处理
arguments = ['Testing: ', 'one', 'two', 'three', '.']
puts join_with_commas1(*arguments)
# Testing: one,two,three.
puts join_with_commas(*arguments)
# Testing: ,one,two,three,.

# * 也可以用作并行赋值：
before, *words, after = ['Testing: ', 'one', 'two', 'three', '.']
puts before
# Testing: 
# words
# ["one", "two", "three"] 此处为irb 的显示，puts 后打印出每个参数的值
puts after
# .


# 6.代码块：为由do/end 或者大括号围住的ruby 代码块，
# 可以带一个隐式代码参数，使用yield 表示对代码块中那段代码的调用
def do_three_times
    yield
    yield
    yield
end

do_three_times{puts 'hello'}
# hello
# hello
# hello

# 代码块可以带参数
def do_three_times1
    yield('first')
    yield('second')
    yield('third')
end

do_three_times1 do
    |n| puts "#{n}: hello"
end
# first: hello
# second: hello
# third: hello

# yield 返回执行代码块的结果
def number_names
    [yield('one'), yield('two'), yield('three')].join(',')
end

puts number_names{ |name| name.upcase.reverse }
# ENO,OWT,EERHT

# 7.枚举类型
# 枚举类型提供提供的方法可以帮助我们对集合进行遍历、搜索和排序
# 在调用的时候，可以带上一个代码块
puts (1..10).count{ |number| number.even?}
puts (1..10).select{ |number| number.even?}
# 2,4,6,8,10
puts (1..10).any? { |number| number < 8}
# 在1~10中，存在数字number < 8？true
puts (1..10).all? { |number| number < 8}
# 在1~10中，所有数字number < 8？false

(1..5).each do |number|
    if number.even?
        puts "#{number} is even"
    else
        puts "#{number} is odd"
    end
end
# 1 is odd
# 2 is even
# 3 is odd
# 4 is even
# 5 is odd

new_arr = (1..10).map { |number| number * 3}
puts new_arr
# [3,6,6,12,15,18,21,24,27,30]

# 一个代码块带有一个参数，并向此参数发送一个无参的消息
# ruby 提供了一种缩写：&:message, 比写{ |object| object.message }
# 于是上面的代码全部可以写成下面的形式：
puts (1..10).select(&:even?)
# [2,4,6,8,10]
puts ['one', 'two', 'three'].map(&:upcase)
# [ONE,TWO,THREE]

# 为集合中的每一个值生成一个数组：
puts ['one', 'two', 'three'].map(&:chars)
# [["o", "n", "e"],["t", "w", "o"],["t", "h", "r", "e", "e"]]
# 将每一个集合中的值连接起来
puts ['one', 'two', 'three'].flat_map(&:chars)
# ["o", "n", "e", "t", "w", "o", "t", "h", "r", "e", "e"]

# 8.inject 方法（类似js 中的可迭代量的reduce 方法）
puts (1..10).inject{ |result, number| result + number}
# 55，未填写初始值，故为空，或者0
puts ['one ', 'two ', 'three']
    .flat_map(&:chars)
    .map(&:upcase)
    .inject { |result, char| result + char}
# ONE TWO THREE

puts (1..10).inject(0) { |result, number| result + number}
# 55, 初始值为0
puts (1..10).inject(1) { |result, number| result * number}
# 362880

puts ['one', 'two', 'three'].inject('Words:') { |result, word| "#{result} #{word}"}
# Words: one two three


# 9.结构体：一种特殊的类，常用来生成别的类（子类）
# 如下为创建一个拥有属性x 和 y，名字为Point 的类
class Point < Struct.new(:x, :y)
    def +(other_point)
        Point.new(x + other_point.x, y + other_point.y)
    end

    def inspect
        "#<Point (#{x}, #{y})>"
    end
end

a = Point.new(2, 3)
# irb: #<Point (2, 3)>
b = Point.new(10, 20)
# irb: #<Point (10, 20)>

puts a + b
# #<Point (12, 23)>

# 获取与设置类中的值 
puts a.x
# 2

a.x = 35
puts a.x
# 35

puts a + b
# #<struct Point x=45, y=23>

# 生成对象的比较：
puts Point.new(4, 5) == Point.new(4, 5)
# true

puts Point.new(3, 4) == Point.new(3, 7)
# false


# 10. 内置对象扩展方法
# 随时可以给已有的类或者模块增加方法，扩展已有类的行为，
# 被称作Monkey Patching
class Point
    def -(other_point)
        Point.new(x - other_point.x, y - other_point.y)
    end
end

puts Point.new(10, 15) - Point.new(2, 3)
# #<struct Point x=8, y=12>

# 甚至可以拓展内置的类方法
class String 
    def shout
        upcase + '!!!'
    end
end

puts 'hello'.shout
# HELLO!!!


# 11.定义常量
# ruby 支持常量，一旦被创建，若是被重新赋值了，会产生警告（不是报错）
# 任何大写字母开头的变量都是常量，可以在顶层或者在一个类或模块中定义新的常量
NUMBER = [4, 8, 15, 16, 23, 42]
class Greeting
    ENGLISH = 'hello'
    FRENCH = 'bonjour'
    GERMAN = 'guten Tag'
end
puts NUMBER.last
# 42

puts Greeting::FRENCH
# bonjour

# 类和模块的名字一般首字母也是大写，所以类和模块的名字也是常量


# 删除常量
Object.send(:remove_const, :NUMBER)
puts NUMBER.last
# Uncaught exception: uninitialized constant NUMBER

Object.send(:remove_const, :Greeting)
puts Greeting::GERMAN
# Uncaught exception: uninitialized constant Greeting